package cn.t.service.device;


import cn.t.core.exception.BizException;
import cn.t.core.mode.YesNoEnum;
import cn.t.core.service.ServiceImpl;
import cn.t.core.enums.base.BaseStatusEnum;
import cn.t.core.enums.base.CommonError;
import cn.t.model.device.DeviceDO;
import cn.t.model.device.DeviceHeartbeatDO;
import cn.t.repository.device.DeviceHeartbeatRepository;
import cn.t.utils.AESUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static cn.t.constants.Constants.REDIS_KEY__DEVICE_TOKEN;


/**
 * @author l
 */
@Slf4j
@Service
@AllArgsConstructor
public class DeviceHeartbeatService extends ServiceImpl<DeviceHeartbeatDO, DeviceHeartbeatRepository> {

    public void save(DeviceHeartbeatDO heartbeat) {

        DeviceDO device = deviceService.getDeviceByImei(heartbeat.getImei());
        if (device == null || device.getPin() == null) {
            String errorMessage = String.format("指定的设备不存在 deviceId=%s", heartbeat.getDeviceId());
            log.error(errorMessage);
            throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
        }

        String token = getToken(heartbeat.getImei(), device.getPin());
        heartbeat.setToken(token);

        Long id = repository.addAndReturnId(heartbeat);
        heartbeat.setId(id);

        deviceService.updateDevice(device.getImei(), new Date());

        String deviceTokenKey = String.format(REDIS_KEY__DEVICE_TOKEN, heartbeat.getDeviceId(), token);
        redissonClient.getBucket(deviceTokenKey).set(heartbeat, 2, TimeUnit.HOURS);

    }

    public String getToken(String imei, String pin) {
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHH");
        String dateStr = format.format(new Date());
        String tokenStr = imei + "_" + dateStr;
        return AESUtil.encrypt(tokenStr, pin);
    }


    public DeviceHeartbeatDO isValidateToken(String imei, String token) {

        DeviceDO device = deviceService.getDeviceByImei(imei);
        if (device == null || device.getPin() == null) {
            String errorMessage = String.format("指定的设备不存在 imei=%s", imei);
            log.error(errorMessage);
            throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
        }

        if (YesNoEnum.TRUE.equals(device.getIsDeleted()) || !BaseStatusEnum.AVAILABLE.getCode().equals(device.getStatus())) {
            String errorMessage = String.format("指定的设备不可用 imei=%s", imei);
            log.error(errorMessage);
            throw new BizException(CommonError.ILLEGAL_PARAMETER, errorMessage);
        }

        DeviceHeartbeatDO previous = findByDeviceAndToken(imei, token);
        if (previous != null) {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.HOUR, -2);
            if (previous.getGmtModified().after(calendar.getTime())) {
                return previous;
            }

        }

        String pin = device.getPin();
        String decryptToken = null;
        String correctToken = null;
        try {
            correctToken = getToken(imei, pin);
            decryptToken = AESUtil.decrypt(token, pin);
        } catch (Throwable e) {
            log.error("token 解密失败 token={},pin={}, decryptToken={}, correctToken={}", token, pin, decryptToken, correctToken, e);
            return null;
        }


        if (StringUtils.isBlank(decryptToken)) {
            return null;
        }


        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
        String dateStr = format.format(new Date());

        if (decryptToken.indexOf(imei + "_" + dateStr) == 0) {

            DeviceHeartbeatDO temp = new DeviceHeartbeatDO();
            temp.setDeviceId(device.getId());
            temp.setImei(imei);
            temp.setToken(token);
            temp.setPin(pin);

            return temp;
        }

        return null;

    }


    public DeviceHeartbeatDO findByDeviceAndToken(String imei, String previousToken) {

        String deviceTokenKey = String.format(REDIS_KEY__DEVICE_TOKEN, imei, previousToken);
        DeviceHeartbeatDO data = (DeviceHeartbeatDO) redissonClient.getBucket(deviceTokenKey).get();
        if (data != null && imei.equals(data.getImei()) && previousToken.equals(data.getToken())) {
            return data;
        }

        DeviceHeartbeatDO template = new DeviceHeartbeatDO();
        template.setImei(imei);
        template.setToken(previousToken);


        List<DeviceHeartbeatDO> tmp = repository.findByModel(template, 0, 1, "gmt_created", "desc");

        if (CollectionUtils.isEmpty(tmp)) {
            return null;
        }

        return tmp.get(0);
    }

    public void refreshToken(DeviceHeartbeatDO previous) {
        DeviceHeartbeatDO template = new DeviceHeartbeatDO();

        previous.setRefreshTimes((previous.getRefreshTimes() != null ? previous.getRefreshTimes() : 0) + 1);

        template.setRefreshTimes(previous.getRefreshTimes());
        template.setId(previous.getId());

        repository.update(template);
        deviceService.updateDevice(previous.getImei(), new Date());

        String deviceTokenKey = String.format(REDIS_KEY__DEVICE_TOKEN, previous.getDeviceId(), previous.getToken());
        redissonClient.getBucket(deviceTokenKey).set(previous, 2, TimeUnit.HOURS);
    }


    @Autowired
    private DeviceService deviceService;

    @Autowired
    private RedissonClient redissonClient;


}
